package com.random.fox.whatspay.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.Calendar;
import java.util.Date;

/**
 * Created by 渠 on 2014/11/7.
 */
public class TimeCircle extends View implements View.OnTouchListener {
    //cos 30
    private static final float COSINE_30_DEGREES = ((float) Math.sqrt(3)) * 0.5f;
    //sin 30
    private static final float SINE_30_DEGREES = 0.5f;

    private static final int DEGREES_FOR_ONE_HOUR = 30;
    private static final int DEGREES_FOR_ONE_MINUTE = 6;

    //数字画笔
    private Paint hourPaint;
    //分钟画笔
    private Paint minutePaint;
    //表盘画笔
    private Paint dialPaint;
    //选中块笔
    private Paint selectorPaint;
    //中心小圆笔
    private Paint centerCirclePaint;
    //分钟选择时的小圆笔
    private Paint minSelectorPaint;

    private String TAG = "TimeCircle";

    int[] mSelectionDegrees = new int[2];


    String[] sHOURS = new String[]{"12", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11"};
    String[] sMINs = new String[]{"00", "05", "10", "15", "20", "25", "30", "35", "40", "45", "50", "55"};

    private float[][] mTextGridHeights = new float[2][7];
    private float[][] mTextGridWidths = new float[2][7];

    private int MODE; //表盘显示的模式，默认是0 hour 小时
    //显示小时模式
    public static int MODE_HOUR = 0;
    //显示分钟模式
    public static int MODE_MIN = 1;

    private int AMorPM; //上午或下午

    public static int AM = Calendar.AM;

    public static int PM = Calendar.PM;

    //表盘的圆的半径
    int dialRadius;
    int XCenter;
    int YCenter;
    //选中状态圆环的半径
    float selectorRadius;
    //数字所在圆环的半径
    float numbersRadius;

    int am_pm = -1;

    public TimeCircle(Context context) {
        super(context);
        init();
    }

    public TimeCircle(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public TimeCircle(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        setBackgroundColor(Color.WHITE);
        initPaint();

        Calendar calendar = Calendar.getInstance();
        setCurrentHour(calendar.get(Calendar.HOUR));
        setCurrentMinute(calendar.get(Calendar.MINUTE));

        AMorPM = calendar.get(Calendar.AM_PM);

        setOnTouchListener(this);
    }

    private void initPaint() {
        hourPaint = new Paint();
        hourPaint.setColor(Color.parseColor("#546463"));
        hourPaint.setTextSize(23);
        hourPaint.setFakeBoldText(true);
        hourPaint.setAntiAlias(true);

        dialPaint = new Paint();
        dialPaint.setAntiAlias(true);
        dialPaint.setColor(Color.parseColor("#EFEFEF"));

        selectorPaint = new Paint();
        selectorPaint.setAntiAlias(true);
        selectorPaint.setStrokeWidth(2); //给画笔一个宽度用于画线
        selectorPaint.setColor(Color.parseColor("#B7DAD7"));

        centerCirclePaint = new Paint();
        centerCirclePaint.setAntiAlias(true);
        centerCirclePaint.setColor(Color.GRAY);

        minSelectorPaint = new Paint();
        minSelectorPaint.setAntiAlias(true);
        minSelectorPaint.setColor(Color.parseColor("#009688"));

        minutePaint = new Paint();
        minutePaint.setColor(Color.parseColor("#546463"));
        minutePaint.setTextSize(23);
        hourPaint.setFakeBoldText(true);
        minutePaint.setAntiAlias(true);

    }


    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        updateLayout();
    }

    private void updateLayout() {
        XCenter = getWidth() / 2;
        YCenter = getHeight() / 2;

        final int min = Math.min(XCenter, YCenter);

        //表盘的半径 = 最小的宽度 - 50
        dialRadius = min - 50;

        //选中状态的圆盘是大圆盘半径的1/7
        selectorRadius = dialRadius * 1 / 6;
        numbersRadius = dialRadius - selectorRadius;

        YCenter = (int) (YCenter - selectorRadius); //上移一点，画AM_PM

        calculateGridSizes(hourPaint, mTextGridHeights[0], mTextGridWidths[0]);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(XCenter, YCenter, dialRadius, dialPaint);


        drawSelector(canvas, MODE);

        if (MODE == MODE_HOUR) {
            drawTextElements(canvas, sHOURS, hourPaint, mTextGridHeights[0], mTextGridWidths[0]);
        } else {
            drawTextElements(canvas, sMINs, minutePaint, mTextGridHeights[0], mTextGridWidths[0]);
        }

        drawAM_PM(canvas);
    }

    //绘制文字元素
    private void drawTextElements(Canvas canvas, String[] texts, Paint paint, float[] textGridHeights, float[] textGridWidths) {
        float offset1 = 0;
        float offset2 = 0;
        //小时的时候需要减去10/11/12三个时间多出来的一个字符的宽度
        //其他偏移半个字符
        if (MODE == MODE_HOUR) {
            float[] w = new float[1];
            paint.getTextWidths("1", w);
            offset1 = w[0];
            offset2 = offset1 / 2;
        } else {
            float[] w = new float[1];
            paint.getTextWidths("1", w);
            offset1 = offset2 = w[0];
        }

        canvas.drawText(texts[0], textGridWidths[3] - offset1, textGridHeights[0], paint);
        canvas.drawText(texts[1], textGridWidths[4] - offset2, textGridHeights[1], paint);
        canvas.drawText(texts[2], textGridWidths[5] - offset2, textGridHeights[2], paint);
        canvas.drawText(texts[3], textGridWidths[6] - offset2, textGridHeights[3], paint);
        canvas.drawText(texts[4], textGridWidths[5] - offset2, textGridHeights[4], paint);
        canvas.drawText(texts[5], textGridWidths[4] - offset2, textGridHeights[5], paint);
        canvas.drawText(texts[6], textGridWidths[3] - offset2, textGridHeights[6], paint);
        canvas.drawText(texts[7], textGridWidths[2] - offset2, textGridHeights[5], paint);
        canvas.drawText(texts[8], textGridWidths[1] - offset2, textGridHeights[4], paint);
        canvas.drawText(texts[9], textGridWidths[0] - offset2, textGridHeights[3], paint);
        canvas.drawText(texts[10], textGridWidths[1] - offset1, textGridHeights[2], paint);
        canvas.drawText(texts[11], textGridWidths[2] - offset1, textGridHeights[1], paint);
    }

    private void calculateGridSizes(Paint paint, float[] textGridHeights, float[] textGridWidths) {
        float yCenter = YCenter;

        float xCenter = XCenter;

        Log.d(TAG, "calculateGridSizes \t" + YCenter + ":" + XCenter);

        final float offset1 = numbersRadius;
        // cos(30) = a / r => r * cos(30)
        final float offset2 = numbersRadius * COSINE_30_DEGREES;
        // sin(30) = o / r => r * sin(30)
        final float offset3 = numbersRadius * SINE_30_DEGREES;

        yCenter -= (paint.descent() + paint.ascent()) / 2;
        textGridHeights[0] = yCenter - offset1;
        textGridWidths[0] = xCenter - offset1;
        textGridHeights[1] = yCenter - offset2;
        textGridWidths[1] = xCenter - offset2;
        textGridHeights[2] = yCenter - offset3;
        textGridWidths[2] = xCenter - offset3;
        textGridHeights[3] = yCenter;
        textGridWidths[3] = xCenter;
        textGridHeights[4] = yCenter + offset3;
        textGridWidths[4] = xCenter + offset3;
        textGridHeights[5] = yCenter + offset2;
        textGridWidths[5] = xCenter + offset2;
        textGridHeights[6] = yCenter + offset1;
        textGridWidths[6] = xCenter + offset1;
    }

    private void drawSelector(Canvas canvas, int mode) {

        float mLineLength = dialRadius - selectorRadius;
        //度数换成弧度
        double selectionRadians = Math.toRadians(mSelectionDegrees[mode]);

        //算出选择的位置的坐标
        int pointX = XCenter + (int) (mLineLength * Math.sin(selectionRadians));
        int pointY = YCenter - (int) (mLineLength * Math.cos(selectionRadians));

        //画选中的大圆
        canvas.drawCircle(pointX, pointY, selectorRadius, selectorPaint);

        //画线
        canvas.drawLine(XCenter, YCenter, pointX, pointY, selectorPaint);

        if (MODE == MODE_MIN) {
            int min = getMinuteFromDegrees(mSelectionDegrees[mode]);
            if (min % 5 != 0) {
                canvas.drawCircle(pointX, pointY, selectorRadius / 4, minSelectorPaint);
            }

        }

        //画中间的点
        canvas.drawCircle(XCenter, YCenter, 3, centerCirclePaint);
    }

    private void drawAM_PM(Canvas canvas) {
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(Color.WHITE);

        float pmX = XCenter + selectorRadius * 5;
        float amX = XCenter - selectorRadius * 5;
        int am_pmY = dialRadius + YCenter;
        if (am_pm == -1) {
            //没有触摸到AM 或者 PM
            if (AM == AMorPM) {
                canvas.drawCircle(pmX, am_pmY, selectorRadius, paint);
                paint.setColor(Color.parseColor("#C3E6E3"));
                canvas.drawCircle(amX, am_pmY, selectorRadius, paint);
            } else {
                canvas.drawCircle(amX, am_pmY, selectorRadius, paint);
                paint.setColor(Color.parseColor("#C3E6E3"));
                canvas.drawCircle(pmX, am_pmY, selectorRadius, paint);
            }
        } else if (am_pm == AM) {
            canvas.drawCircle(pmX, am_pmY, selectorRadius, paint);
            paint.setColor(Color.parseColor("#009688"));
            canvas.drawCircle(amX, am_pmY, selectorRadius, paint);
        } else {
            canvas.drawCircle(amX, am_pmY, selectorRadius, paint);
            paint.setColor(Color.parseColor("#009688"));
            canvas.drawCircle(pmX, am_pmY, selectorRadius, paint);
        }

        float offsetY = -(minutePaint.ascent() + minutePaint.descent()) / 2;
        float offsetX = minutePaint.measureText("A");

        canvas.drawText("AM", amX - offsetX, am_pmY + offsetY, minutePaint);
        canvas.drawText("PM", pmX - offsetX, am_pmY + offsetY, minutePaint);
    }

    public void setCurrentHour(int hour) {
        final int degrees = (hour % 12) * DEGREES_FOR_ONE_HOUR;
        mSelectionDegrees[MODE_HOUR] = degrees;
        invalidate();
    }

    public void setCurrentMinute(int minute) {
        mSelectionDegrees[MODE_MIN] = (minute % 60) * DEGREES_FOR_ONE_MINUTE;
        invalidate();
    }

    public void showHour() {
        MODE = MODE_HOUR;
        invalidate();
    }

    public void showMinute() {
        MODE = MODE_MIN;
        invalidate();
    }


    @Override
    public boolean onTouch(View v, MotionEvent event) {
        final float eventX = event.getX();
        final float eventY = event.getY();
        boolean result = false;

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                am_pm = getIsTouchingAmOrPm(eventX, eventY);
                if (am_pm != -1) {
                    AMorPM = am_pm;
                    invalidate();
                    result = true;
                    break;
                }

                int degrees = getDegreesFromXY(eventX, eventY);
                if (degrees != -1) {
                    result = true;
                    if (MODE == MODE_HOUR) {
                        //显示小时需要进行30度近似判断
                        setCurrentHour(getHourFromDegrees(degrees));
                    } else {
                        //显示分钟需要进行分钟近似判断
                        setCurrentMinute(getMinuteFromDegrees(degrees));
                    }
                    invalidate();
                }
                break;
            case MotionEvent.ACTION_UP:
                am_pm = -1;
                if (MODE == MODE_HOUR && getDegreesFromXY(eventX, eventY) != -1)
                    showMinute();
                invalidate();
                break;

        }
        return result;
    }

    private int getIsTouchingAmOrPm(float x, float y) {
        float amX = XCenter - selectorRadius * 5;
        float pmX = XCenter + selectorRadius * 5;
        float Y = dialRadius + YCenter;
        float squaredY = (Y - y) * (Y - y);

        int distanceToAmCenter = (int) Math.sqrt((x - amX) * (x - amX) + squaredY);

        if (distanceToAmCenter < selectorRadius) {
            return AM;
        }

        int distanceToPmCenter = (int) Math.sqrt((x - pmX) * (x - pmX) + squaredY);

        if (distanceToPmCenter < selectorRadius) {
            return PM;
        }

        return -1;
    }

    private int getHourFromDegrees(int degree) {
        Log.d(TAG, "getHourFromDegrees:" + degree);
        int index = (degree + 15) / DEGREES_FOR_ONE_HOUR;
        if (index == sHOURS.length)
            index = 0;
        return Integer.parseInt(sHOURS[index]);
    }

    private int getMinuteFromDegrees(int degree) {
        int min = degree / DEGREES_FOR_ONE_MINUTE;
        Log.d(TAG, "getMinute:" + min);
        return min;
    }

    public int getCurrentHour(){
        return getHourFromDegrees(mSelectionDegrees[MODE_HOUR]);
    }

    public int getCurrentMinute(){
        return getMinuteFromDegrees(mSelectionDegrees[MODE_MIN]);
    }

    /**
     * 获得当前时钟显示的时间
     * @return
     */
    public Date getDate(){
        Calendar calendar = Calendar.getInstance();
        calendar.set(Calendar.AM_PM,AMorPM);
        calendar.set(Calendar.HOUR,getCurrentHour());
        calendar.set(Calendar.MINUTE,getCurrentMinute());
        return calendar.getTime();
    }


    private int getDegreesFromXY(float x, float y) {
        final double hypotenuse = Math.sqrt(
                (y - YCenter) * (y - YCenter) + (x - XCenter) * (x - XCenter));

        // 大于表盘，按到外面了
        if (hypotenuse > dialRadius) {
            return -1;
        }

        //比数字表盘还小很多
        if (hypotenuse < numbersRadius - 20) {
            return -1;
        }

        final float opposite = Math.abs(y - YCenter);
        double degrees = Math.toDegrees(Math.asin(opposite / hypotenuse));

        boolean rightSide = (x > XCenter);
        boolean topSide = (y < YCenter);
        if (rightSide && topSide) {
            degrees = 90 - degrees;
        } else if (rightSide && !topSide) {
            degrees = 90 + degrees;
        } else if (!rightSide && !topSide) {
            degrees = 270 - degrees;
        } else if (!rightSide && topSide) {
            degrees = 270 + degrees;
        }
        return (int) degrees;
    }

}
